package studio.baka.satoripixeldungeon.items.artifacts;

import studio.baka.satoripixeldungeon.Assets;
import studio.baka.satoripixeldungeon.Dungeon;
import studio.baka.satoripixeldungeon.actors.buffs.Blindness;
import studio.baka.satoripixeldungeon.actors.buffs.LockedFloor;
import studio.baka.satoripixeldungeon.actors.hero.Hero;
import studio.baka.satoripixeldungeon.effects.particles.ElmoParticle;
import studio.baka.satoripixeldungeon.items.Generator;
import studio.baka.satoripixeldungeon.items.Item;
import studio.baka.satoripixeldungeon.items.scrolls.*;
import studio.baka.satoripixeldungeon.items.scrolls.exotic.ExoticScroll;
import studio.baka.satoripixeldungeon.messages.Messages;
import studio.baka.satoripixeldungeon.scenes.GameScene;
import studio.baka.satoripixeldungeon.sprites.ItemSpriteSheet;
import studio.baka.satoripixeldungeon.utils.GLog;
import studio.baka.satoripixeldungeon.windows.WndBag;
import studio.baka.satoripixeldungeon.windows.WndOptions;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
import com.watabou.utils.Reflection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;

@SuppressWarnings("rawtypes")
public class UnstableSpellbook extends Artifact {

    {
        image = ItemSpriteSheet.ARTIFACT_SPELLBOOK;

        levelCap = 10;

        charge = (int) (level() * 0.6f) + 2;
        partialCharge = 0;
        chargeCap = (int) (level() * 0.6f) + 2;

        defaultAction = AC_READ;
    }

    public static final String AC_READ = "READ";
    public static final String AC_ADD = "ADD";

    private final ArrayList<Class> scrolls = new ArrayList<>();

    protected WndBag.Mode mode = WndBag.Mode.SCROLL;

    public UnstableSpellbook() {
        super();

        Class<?>[] scrollClasses = Generator.Category.SCROLL.classes;
        float[] probs = Generator.Category.SCROLL.probs.clone(); //array of primitives, clone gives deep copy.
        int i = Random.chances(probs);

        while (i != -1) {
            scrolls.add(scrollClasses[i]);
            probs[i] = 0;

            i = Random.chances(probs);
        }
        scrolls.remove(ScrollOfTransmutation.class);
    }

    @Override
    public ArrayList<String> actions(Hero hero) {
        ArrayList<String> actions = super.actions(hero);
        if (isEquipped(hero) && charge > 0 && !cursed)
            actions.add(AC_READ);
        if (isEquipped(hero) && level() < levelCap && !cursed)
            actions.add(AC_ADD);
        return actions;
    }

    @Override
    public void execute(Hero hero, String action) {

        super.execute(hero, action);

        if (action.equals(AC_READ)) {

            if (hero.buff(Blindness.class) != null) GLog.w(Messages.get(this, "blinded"));
            else if (!isEquipped(hero)) GLog.i(Messages.get(Artifact.class, "need_to_equip"));
            else if (charge <= 0) GLog.i(Messages.get(this, "no_charge"));
            else if (cursed) GLog.i(Messages.get(this, "cursed"));
            else {
                charge--;

                Scroll scroll;
                do {
                    scroll = (Scroll) Generator.random(Generator.Category.SCROLL);
                } while (scroll == null
                        //reduce the frequency of these scrolls by half
                        || ((scroll instanceof ScrollOfIdentify ||
                        scroll instanceof ScrollOfRemoveCurse ||
                        scroll instanceof ScrollOfMagicMapping) && Random.Int(2) == 0)
                        //don't roll teleportation scrolls on boss floors
                        || (scroll instanceof ScrollOfTeleportation && Dungeon.bossLevel())
                        || (scroll instanceof ScrollOfTransmutation));

                scroll.anonymize();
                curItem = scroll;
                curUser = hero;

                //if there are changes left and the scroll has been given to the book
                if (charge > 0 && !scrolls.contains(scroll.getClass())) {
                    final Scroll fScroll = scroll;
                    GameScene.show(new WndOptions(
                            Messages.get(this, "prompt"),
                            Messages.get(this, "read_empowered"),
                            scroll.trueName(),
                            Messages.get(ExoticScroll.regToExo.get(scroll.getClass()), "name")) {
                        @Override
                        protected void onSelect(int index) {
                            if (index == 1) {
                                Scroll scroll = Reflection.newInstance(ExoticScroll.regToExo.get(fScroll.getClass()));
                                charge--;
                                Objects.requireNonNull(scroll).doRead();
                            } else {
                                fScroll.doRead();
                            }
                        }

                        @Override
                        public void onBackPressed() {
                            //do nothing
                        }
                    });
                } else {
                    scroll.doRead();
                }
                updateQuickslot();
            }

        } else if (action.equals(AC_ADD)) {
            GameScene.selectItem(itemSelector, mode, Messages.get(this, "prompt"));
        }
    }

    @Override
    protected ArtifactBuff passiveBuff() {
        return new bookRecharge();
    }

    @Override
    public void charge(Hero target) {
        if (charge < chargeCap) {
            partialCharge += 0.1f;
            if (partialCharge >= 1) {
                partialCharge--;
                charge++;
                updateQuickslot();
            }
        }
    }

    @Override
    public Item upgrade() {
        chargeCap = (int) ((level() + 1) * 0.6f) + 2;

        //for artifact transmutation.
        while (!scrolls.isEmpty() && scrolls.size() > (levelCap - 1 - level()))
            scrolls.remove(0);

        return super.upgrade();
    }

    @Override
    public String desc() {
        String desc = super.desc();

        if (isEquipped(Dungeon.hero)) {
            if (cursed) {
                desc += "\n\n" + Messages.get(this, "desc_cursed");
            }

            if (level() < levelCap && scrolls.size() > 0) {
                desc += "\n\n" + Messages.get(this, "desc_index");
                desc += "\n" + "_" + Messages.get(scrolls.get(0), "name") + "_";
                if (scrolls.size() > 1)
                    desc += "\n" + "_" + Messages.get(scrolls.get(1), "name") + "_";
            }
        }

        if (level() > 0) {
            desc += "\n\n" + Messages.get(this, "desc_empowered");
        }

        return desc;
    }

    private static final String SCROLLS = "scrolls";

    @Override
    public void storeInBundle(Bundle bundle) {
        super.storeInBundle(bundle);
        bundle.put(SCROLLS, scrolls.toArray(new Class[0]));
    }

    @Override
    public void restoreFromBundle(Bundle bundle) {
        super.restoreFromBundle(bundle);
        scrolls.clear();
        Collections.addAll(scrolls, bundle.getClassArray(SCROLLS));
    }

    public class bookRecharge extends ArtifactBuff {
        @Override
        public boolean act() {
            LockedFloor lock = target.buff(LockedFloor.class);
            if (charge < chargeCap && !cursed && (lock == null || lock.regenOn())) {
                partialCharge += 1 / (120f - (chargeCap - charge) * 5f);

                if (partialCharge >= 1) {
                    partialCharge--;
                    charge++;

                    if (charge == chargeCap) {
                        partialCharge = 0;
                    }
                }
            }

            updateQuickslot();

            spend(TICK);

            return true;
        }
    }

    protected WndBag.Listener itemSelector = item -> {
        if (item instanceof Scroll && item.isIdentified()) {
            Hero hero = Dungeon.hero;
            for (int i = 0; (i <= 1 && i < scrolls.size()); i++) {
                if (scrolls.get(i).equals(item.getClass())) {
                    hero.sprite.operate(hero.pos);
                    hero.busy();
                    hero.spend(2f);
                    Sample.INSTANCE.play(Assets.SND_BURNING);
                    hero.sprite.emitter().burst(ElmoParticle.FACTORY, 12);

                    scrolls.remove(i);
                    item.detach(hero.belongings.backpack);

                    upgrade();
                    GLog.i(Messages.get(UnstableSpellbook.class, "infuse_scroll"));
                    return;
                }
            }
            GLog.w(Messages.get(UnstableSpellbook.class, "unable_scroll"));
        } else if (item instanceof Scroll && !item.isIdentified())
            GLog.w(Messages.get(UnstableSpellbook.class, "unknown_scroll"));
    };
}
